﻿using Devonline.Entity;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

namespace Devonline.Communication;

/// <summary>
/// 消息集线器
/// string 作为主键类型
/// </summary>
/// <typeparam name="T"></typeparam>
public class MessageCenter<TMessage, T> : CommunicationCenter<IMessageClient<T>, T> where TMessage : Message<T>
{
    public MessageCenter(
        ILogger<CommunicationCenter<IMessageClient<T>, T>> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        HostSetting hostSetting
        ) : base(logger, cache, httpContextAccessor, hostSetting)
    {
    }

    #region 供客户端调用的公开方法
    /// <summary>
    /// 服务器转发消息的方法
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    public virtual async Task SendMessage(Message<T> message)
    {
        try
        {
            //验证消息体
            ValidateMessage(message);

            //处理消息
            await InitialMessage(message);

            //发送消息到接收者
            if (string.IsNullOrWhiteSpace(message.Receiver))
            {
                throw new ArgumentNullException(nameof(message), "The message has no receiver!");
            }

            await Clients.Group(message.Receiver).Receive(message);

            _logger.LogInformation("The client {clientId} Send message to receiver {receiver}", ClientId, message.Receiver);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "The client {clientId} Send message {message} to receiver {receiver} throw exception!", ClientId, message.ToJsonString(), message.Receiver);
        }
    }
    /// <summary>
    /// 服务器批量转发消息的方法
    /// </summary>
    /// <param name="messages"></param>
    /// <returns></returns>
    public virtual async Task SendMessages(IEnumerable<Message<T>> messages)
    {
        var tasks = new List<Task>(messages.Count());
        foreach (var item in messages)
        {
            var task = new Task(async x =>
            {
                if (x is Message<T> message)
                {
                    try
                    {
                        //验证消息体
                        ValidateMessage(message);

                        //处理消息
                        await InitialMessage(message);

                        //发送消息到接收者
                        if (string.IsNullOrWhiteSpace(message.Receiver))
                        {
                            throw new ArgumentNullException(nameof(message), "The message has no receiver!");
                        }

                        await Clients.Group(message.Receiver).Receive(message);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "The client {clientId} Send message {message} to receiver {receiver} throw exception!", ClientId, message.ToJsonString(), message.Receiver);
                    }
                }
            }, item);

            task.Start();
            tasks.Add(task);
        }

        await Task.WhenAll(tasks.ToArray()).ContinueWith(x => _logger.LogInformation("The client {clientId} Send messages to receivers", ClientId));
    }

    /// <summary>
    /// 发送通知消息到指定客户端
    /// 服务器暂不接受通知回执
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    public virtual async Task Notice(Message<T> message)
    {
        try
        {
            //验证消息体
            ValidateMessage(message);

            //处理消息
            message.Type = MessageType.Notice;
            await InitialMessage(message);

            //发送消息到接收者
            if (string.IsNullOrWhiteSpace(message.Receiver))
            {
                throw new ArgumentNullException(nameof(message), "The message has no receiver!");
            }

            await Clients.Group(message.Receiver).Notice(message);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "The client {clientId} Send message {message} to receiver {receiver} throw exception!", ClientId, message.ToJsonString(), message.Receiver);
        }
    }
    /// <summary>
    /// 发送预警消息到指定客户端
    /// 服务器暂不接受预警回执
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    public virtual async Task Alarm(Message<T> message)
    {
        try
        {
            //验证消息体
            ValidateMessage(message);

            //处理消息
            message.Type = MessageType.Alarm;
            await InitialMessage(message);

            //发送消息到接收者
            if (string.IsNullOrWhiteSpace(message.Receiver))
            {
                throw new ArgumentNullException(nameof(message), "The message has no receiver!");
            }

            await Clients.Group(message.Receiver).Alarm(message);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "The client {clientId} Send message {message} to receiver {receiver} throw exception!", ClientId, message.ToJsonString(), message.Receiver);
        }
    }
    /// <summary>
    /// 发送回复消息到指定客户端
    /// 服务器暂不接受预警回执
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    public virtual async Task Reply(Message<T> message)
    {
        try
        {
            //验证消息体
            ValidateMessage(message);

            //处理消息
            message.Type = MessageType.Reply;
            await InitialMessage(message);

            //发送消息到接收者
            if (string.IsNullOrWhiteSpace(message.Receiver))
            {
                throw new ArgumentNullException(nameof(message), "The message has no receiver!");
            }

            await Clients.Group(message.Receiver).Reply(message);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "The client {clientId} Send message {message} to receiver {receiver} throw exception!", ClientId, message.ToJsonString(), message.Receiver);
        }
    }

    /// <summary>
    /// 服务器接收消息回执方法
    /// </summary>
    /// <param name="id">消息编号</param>
    /// <returns></returns>
    public virtual async Task Ack(string id)
    {
        try
        {
            if (id.IsNullOrWhiteSpace())
            {
                throw new Exception("The message id can not be null or whitespace!");
            }

            var messageKey = _hostSetting.CacheKeyPrefix + id;
            var cache = await _cache.GetStringAsync(messageKey);
            if (string.IsNullOrWhiteSpace(cache))
            {
                throw new Exception($"The message of id {id} not found!");
            }

            var message = cache.ToJsonObject<Message<T>>();
            if (message == null)
            {
                throw new Exception($"The message of id {id} not found!");
            }

            message.ReceiveTime = DateTime.UtcNow;
            _logger.LogInformation("Receive the ACK message from {receiver} to {sender}", message.Receiver, message.Sender);

            //保存回执状态
            await _cache.SetStringAsync(messageKey, message.ToJsonString());

            //回执发送消息到发送者
            if (string.IsNullOrWhiteSpace(message.Sender))
            {
                throw new ArgumentNullException(nameof(message), "The message has no sender!");
            }

            await Clients.Group(message.Sender).Ack(id);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Receive and send ACK message from {receiver} throw exception!", ClientId);
        }
    }
    /// <summary>
    /// 服务器接收消息已读方法
    /// </summary>
    /// <param name="id">消息编号</param>
    /// <returns></returns>
    public virtual async Task Read(string id)
    {
        try
        {
            if (id.IsNullOrWhiteSpace())
            {
                throw new Exception("The message id can not be null or whitespace!");
            }

            var messageKey = _hostSetting.CacheKeyPrefix + id;
            var cache = await _cache.GetStringAsync(messageKey);
            if (string.IsNullOrWhiteSpace(cache))
            {
                throw new Exception($"The message of id {id} not found!");
            }

            var message = cache.ToJsonObject<Message<T>>();
            if (message == null)
            {
                throw new Exception($"The message of id {id} not found!");
            }

            message.IsRead = true;
            message.ReadTime = DateTime.UtcNow;
            _logger.LogInformation("Read message from {receiver} to {sender}", message.Receiver, message.Sender);

            //保存已读的消息
            await _cache.SetStringAsync(messageKey, message.ToJsonString());

            //将消息已读状态发送到发送者
            if (string.IsNullOrWhiteSpace(message.Sender))
            {
                throw new ArgumentNullException(nameof(message), "The message has no sender!");
            }

            await Clients.Group(message.Sender).Read(id);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Read and send message from {receiver} throw exception!", ClientId);
        }
    }
    /// <summary>
    /// 服务器接收心跳消息, 心跳消息为客户端保活信号
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    public virtual async Task Heartbeat()
    {
        try
        {
            var message = new Message<T>
            {
                Id = KeyGenerator.GetKey().ToString(),
                Type = MessageType.Heartbeat,
                Sender = ClientId,
                CreateTime = DateTime.UtcNow
            };

            _logger.LogInformation("Receive the Heartbeat message from {client}", message.Sender);
            var messageKey = _hostSetting.CacheKeyPrefix + MessageType.Heartbeat.ToString() + AppSettings.CHAR_UNDERLINE + message.Sender;
            await _cache.SetStringAsync(messageKey, message.ToJsonString());
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Receive the Heartbeat message from {client} throw exception!", ClientId);
        }
    }
    #endregion

    #region 不公开的方法
    /// <summary>
    /// 验证消息体
    /// </summary>
    /// <param name="message"></param>
    /// <exception cref="Exception"></exception>
    protected virtual void ValidateMessage(Message<T> message)
    {
        if (message == null)
        {
            throw new ArgumentNullException(nameof(message), "The message can't be null");
        }

        if (message.Content == null)
        {
            throw new ArgumentNullException(nameof(message.Content), "The message content can't be null");
        }

        if (string.IsNullOrWhiteSpace(message.Receiver) && string.IsNullOrWhiteSpace(message.Group))
        {
            throw new ArgumentNullException(nameof(message), "The message has no receiver or receive group");
        }
    }
    /// <summary>
    /// 通用消息处理
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    protected virtual async Task InitialMessage(Message<T> message)
    {
        //填充消息编号和消息当前连接
        message.Id ??= KeyGenerator.GetKey().ToString();
        message.Sender ??= ClientId;
        message.SendTime ??= DateTime.UtcNow;

        //将收到的消息先保存到缓存中
        var content = message.ToJsonString();
        var receiver = message.Group ?? string.Empty;
        receiver += (receiver.IsNullOrWhiteSpace() ? string.Empty : "@") + (message.Receiver ?? Receiver);

        _logger.LogInformation("Communication Center receive the {messageType} message {message} from {sender} to {receiver}", message.Type, content, message.Sender, receiver);
        var messageKey = _hostSetting.CacheKeyPrefix + message.Id;
        await _cache.SetStringAsync(messageKey, content);
    }
    #endregion
}

/// <summary>
/// 默认的字符串内容消息集线器
/// string 作为内容类型
/// </summary>
public class MessageCenter : MessageCenter<Message<string>, string>
{
    public MessageCenter(ILogger<CommunicationCenter<IMessageClient<string>, string>> logger, IDistributedCache cache, IHttpContextAccessor httpContextAccessor, HostSetting hostSetting) : base(logger, cache, httpContextAccessor, hostSetting)
    {
    }
}